﻿namespace OasisCiq.Xml
{
    // ReSharper disable MemberCanBePrivate.Global

    using System.Collections.Generic;
    using System.Xml;
    using CommonTypes;
    using Xal;
    using Xnal;
    using Xnl;

    public class XnalXmlReader
    {
        private const string XmlNS = XmlNamespaces.NameAndAddress;
        private readonly XmlReader reader;
        private readonly CommonTypesXmlReader ctReader = new CommonTypesXmlReader();
        private readonly XnlXmlReader xnlReader;
        private readonly XalXmlReader xalReader;

        public XnalXmlReader(XmlReader reader)
        {
            this.reader = reader;
            xnlReader = new XnlXmlReader(reader);
            xalReader = new XalXmlReader(reader);
        }

        public Record ReadRecord()
        {
            var element = new Record();
            return ReadRecord(element, "Record", XmlNS) ? element : null;
        }

        public bool ReadRecord(Record element, string elementName, string elementNamespace)
        {
            if (element == null)
            {
                throw new System.ArgumentNullException("element");
            }

            if (!CheckElement(elementName, elementNamespace))
            {
                return false;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            attrs = ctReader.ReadValidityDateGroup(element, attrs);
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ctReader.ReadLanguageCodeGroup(element, attrs);
            attrs = ReadRecordAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first child
            while (xnlReader.CheckElement("PartyName"))
            {
                if (element.PartyNames == null)
                {
                    element.PartyNames = new List<PartyNameType>();
                }

                var partyName = xnlReader.ReadPartyName();
                element.PartyNames.Add(partyName);
            }

            while (xalReader.CheckElement("Address"))
            {
                if (element.Addresses == null)
                {
                    element.Addresses = new List<AddressType>();
                }

                var address = xalReader.ReadAddress();
                element.Addresses.Add(address);
            }

            reader.Read(); // from xnal:Record end element to next node
            return true;
        }

        public PostalLabel ReadPostalLabel()
        {
            var element = new PostalLabel();
            return ReadPostalLabel(element, "PostalLabel", XmlNS) ? element : null;
        }

        public bool ReadPostalLabel(PostalLabel element, string elementName, string elementNamespace)
        {
            if (element == null)
            {
                throw new System.ArgumentNullException("element");
            }

            if (!CheckElement(elementName, elementNamespace))
            {
                return false;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            attrs = ctReader.ReadValidityDateGroup(element, attrs);
            attrs = ctReader.ReadDataQualityGroup(element, attrs);
            attrs = ctReader.ReadLanguageCodeGroup(element, attrs);
            attrs = ReadPostalLabelAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            reader.Read(); // to first child
            while (CheckElement("Addressee"))
            {
                if (element.Addressees == null)
                {
                    element.Addressees = new List<Addressee>();
                }

                var addressee = ReadAddressee();
                element.Addressees.Add(addressee);
            }

            element.Address = xalReader.ReadAddress();
            reader.Read(); // from xnal:PostalLabel end element to next node
            return true;
        }

        public Addressee ReadAddressee()
        {
            if (!CheckElement("Addressee"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new Addressee();
            attrs = ctReader.ReadLanguageCodeGroup(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            if (!reader.IsEmptyElement)
            {
                reader.Read(); // to first child
                while (CheckElement("Designation"))
                {
                    if (element.Designations == null)
                    {
                        element.Designations = new List<Designation>();
                    }

                    var designation = ReadDesignation();
                    element.Designations.Add(designation);
                }

                element.PartyName = xnlReader.ReadPartyName();
                while (CheckElement("DependencyName"))
                {
                    if (element.DependencyNames == null)
                    {
                        element.DependencyNames = new List<DependencyName>();
                    }

                    var dependency = ReadDependencyName();
                    element.DependencyNames.Add(dependency);
                }
            }

            reader.Read(); // from xnal:Addressee end or empty element to next node
            return element;
        }

        public DependencyName ReadDependencyName()
        {
            if (!CheckElement("DependencyName"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var element = new DependencyName();
            attrs = xnlReader.ReadPartyNameAttributesWhithoutExtendedAttributes(element, attrs);
            attrs = ReadDependencyNameAttributes(element, attrs);
            ctReader.ReadExtendedAttributes(element, attrs);
            xnlReader.ReadPartyNameChildren(element);
            reader.Read(); // from xnal:DependencyName end or empty element to next node
            return element;
        }

        public Designation ReadDesignation()
        {
            if (!CheckElement("Designation"))
            {
                return null;
            }

            var attrs = _XmlAttribute.GetAttributes(reader);
            var value = reader.ReadElementContentAsString();
            var element = new Designation(value);
            ctReader.ReadExtendedAttributes(element, attrs);
            return element;
        }

        public IEnumerable<_XmlAttribute> ReadDependencyNameAttributes(
           DependencyName element,
           IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Type":
                            element.Type = new DependencyTypeList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadPostalLabelAttributes(
           PostalLabel element,
           IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "Status":
                            element.Status = new StatusList(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public IEnumerable<_XmlAttribute> ReadRecordAttributes(
          Record element,
          IEnumerable<_XmlAttribute> attributes)
        {
            foreach (var xmlAttribute in attributes)
            {
                if (xmlAttribute.NS == XmlNS)
                {
                    switch (xmlAttribute.Name)
                    {
                        case "RecordID":
                            element.RecordId = new CTString(xmlAttribute.Value);
                            continue;
                        case "RecordIDType":
                            element.RecordIdType = new RecordIdTypeList(xmlAttribute.Value);
                            continue;
                        case "Status":
                            element.Status = new StatusList(xmlAttribute.Value);
                            continue;
                        case "RecordKey":
                            element.RecordKey = new CTString(xmlAttribute.Value);
                            continue;
                        case "RecordKeyRef":
                            element.RecordKeyRef = new CTString(xmlAttribute.Value);
                            continue;
                    }
                }

                yield return xmlAttribute;
            }
        }

        public bool CheckElement(string name)
        {
            return CheckElement(name, XmlNS);
        }

        public bool CheckElement(string name, string @namespace)
        {
            var ns = reader.NamespaceURI;
            var elem = reader.NodeType == XmlNodeType.Element ? reader.LocalName : null;
            return elem != null && ns == @namespace && elem == name;
        }
    }
}
